package com.lccviolet;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author LiChaochao
 * @Date 2021/9/2 10:39
 * @Description VolatileDemo volatile案例,验证可见性,原子性
 */
public class VolatileDemo {
    public static void main(String[] args) {
        atomicityTest();
//        visibilityTest();
    }

    /*
    * 验证volatile不保证原子性：
    *   new200个线程，每个线程让资源类变量++1000次，最后取值理应为200 * 1000；然而结果却<=理论值
    * */
    private static void atomicityTest() {
        MyData myData = new MyData();

        for (int i = 0; i < 200; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlus();
                    myData.addAtomic();
                }
            }, String.valueOf(i)).start();
        }

        // 后台默认有两个线程：一个main，一个GC
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + myData.num);
        System.out.println(Thread.currentThread().getName() + myData.atomicInteger);
    }

    /*
     * 验证volatile的可见性：
     *   变量不加volatile，线程A修改完之后，没人通知main线程，导致main线程陷入死循环；
     *   加了volatile之后，线程A刚修改完，main线程就知道了，因此跳出while循环，输出变量值
     * */
    private static void visibilityTest() {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " come in...");
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            myData.addNum();
            System.out.println(Thread.currentThread().getName() + " update value:" + myData.num);
        }, "ThreadA").start();

        while (myData.num == 0) {

        }

        System.out.println(Thread.currentThread().getName() + " get number value:" + myData.num);
    }
}

class MyData {
//    int num = 0;
    volatile int num = 0;

    public void addNum() {
        this.num = 46;
    }

    public void addPlus() {
        // ++操作在底层不是原子的，而是被拆分为3步
        this.num++;
    }

    /*
    * 解决原子性问题
    * */
    // 不指定初始值的话，默认值为0
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addAtomic() {
        // getAndIncrement()即i++
        atomicInteger.getAndIncrement();
    }
}

